/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.isd.global.dao;

/**
 *
 * @author ctrosch
 */

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import com.isd.global.excepciones.PreexistingEntityException;
import com.isd.global.excepciones.RollbackFailureException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class BaseDAO {
    
    protected EntityManager em;

    @PersistenceContext(unitName="logic")
    public void setEm(EntityManager em){
	this.em = em;
    }

    public EntityManager getEntityManager() {
        return em;
    }

   
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void crear(Object objeto) throws Exception{
        
        em = getEntityManager();
        em.persist(objeto);
        em.flush();
        
    }

   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public void editar(Object objeto)throws Exception {

       em = getEntityManager();
       em.merge(objeto);
       em.flush();

    }


   /**
    * Eliminar objeto con clave primaria Integer
    * @param entityClass  Clase del objeto a eliminar
    * @param id Valor de la clave primaria
    */
   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public void eliminar(Class entityClass, Object id) throws PreexistingEntityException, RollbackFailureException, Exception {

       try {
//            utx.begin();

            em = getEntityManager();
            em.remove(em.find(entityClass, id));

//            utx.commit();

        }catch(Exception ex)  {

            throw ex;

        } 
   }

   /**
    * Eliminar objeto pasando el objeto persistente
    * @param objeto Objeto a eliminar
    * 
    */
   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public void eliminar(Object objeto) throws RollbackFailureException, Exception{

       try {
//            utx.begin();

            em = getEntityManager();
            em.remove(objeto);

//            utx.commit();

        }catch(Exception ex)  {

            throw ex;

        } 
   }

    /**
    * Eliminar objeto con clave primaria Short
    * @param entityClass  Clase del objeto a eliminar
    * @param id Valor de la clave primaria
    */
//    public void eliminar(Class entityClass, Short id) {
//       try{
//           em.remove(getEm().find(entityClass, id));
//           em.flush();
//       }catch(Exception e){

//       }
//    }

   /**
   * Eliminar objeto con clave primaria String
   * @param entityClass  Clase del objeto a eliminar
   * @param id Valor de la clave primaria
   */
//   public void eliminar(Class entityClass, String cod) {
//       try{
//           em.remove(getEm().find(entityClass, cod));
//           em.flush();
//       }catch(Exception e){
//       }
//   }


   /**
    * Eliminar objeto con clave primaria Integer
    * @param entityClass  Clase del objeto a eliminar
    * @param id Objeto clave primaria
    */
//   public void eliminar(Class entityClass, Object id) {
//
//       em.remove(getEm().find(entityClass, id));
//       em.flush();
//   }

   public <T extends Object> T getObjeto(Class<T> entityClass,Object id){

       try {
            em.flush();
            return (T) em.find(entityClass,id);
     
       }catch(javax.persistence.NoResultException nre){
            return null;
        }catch (Exception e){
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "getObjeto", e);                        
            return null;
        }
   }

   public <T extends Object> T getObjeto(Class<T> entityClass,Integer id){

       try {
            em.flush();
            return (T) em.find(entityClass,id);

       }catch(javax.persistence.NoResultException nre){
            return null;            
        }catch (Exception e){
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "getObjeto", e);                                    
            return null;
        }
   }

   public <T extends Object> T getObjeto(Class<T> entityClass,String cod){

       try {
            em.flush();
            return (T) em.find(entityClass,cod);
        
       }catch(javax.persistence.NoResultException nre){
            return null;
       
       }catch (Exception e){
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "getObjeto", e);                                    
            return null;
        }
   }

   public <T extends Object> T getObjeto(Class<T> entityClass,Short id){

       try {
            em.flush();
            return (T) em.find(entityClass,id);
        
       }catch(javax.persistence.NoResultException nre){
            return null;            
        }catch (Exception e){
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "getObjeto", e);                                    
            return null;
        }
   }

   /**
    * Se obtiene una entidad de acuerdo
    * a un tipo de valor específico para
    * un campo determinado
    *
    * @param <T>
    * @param entityClass: Clase de objeto a obtener
    * @param nombreCampo: Nombre del campo por el cual se buscarán coincidencias
    * @param valorCampo:  Valor del campo a obtener
    * @return un objeto del tipo entityClass
    */
   public <T extends Object> T getObjeto(Class<T> entityClass, String nombreCampo, String valorCampo){

        try {            
            String string = "SELECT o FROM "+entityClass.getSimpleName() +" o WHERE o."+ nombreCampo+" ='" + valorCampo + "'";
            return  (T) em.createQuery("SELECT o FROM "+entityClass.getSimpleName() +" o WHERE o."+ nombreCampo+" ='" + valorCampo + "'").getSingleResult();
        }catch(javax.persistence.NoResultException nre){
            return null;
        }catch (Exception e) {            
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "getObjeto", e);                                    
            return null;
        }
   }
   
   public <T extends Object> T getObjeto(Class<T> entityClass, Map<String,String> filtro){

        try {
            String sQuery = "SELECT o FROM "+entityClass.getSimpleName() +" o ";
            
            //Si el filtro no está vacio lo aplicamos
            if (filtro.isEmpty()){
                
                sQuery = sQuery + "WHERE ";

                Iterator it = filtro.entrySet().iterator();
                while(it.hasNext()){
                    Map.Entry ent = (Map.Entry)it.next();
                    sQuery = sQuery + " o."+ ent.getKey() +" ='" + ent.getValue() + "' ";
                }
            }

            return  (T) em.createQuery(sQuery).getSingleResult();
        }catch(javax.persistence.NoResultException nre){
            return null;
        }catch (Exception e) {            
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "getObjeto", e);           
            return null;
        }
   }

   /**
    * Consulta todos los registro de una entidad particular
    *
    * @param <T>
    * @param entityClass: Clase de la entidad a consultar
    * @param all: indica si se devuelven todos los registro
    * @param maxResults: cantidad de registros a devolver
    * @param maxResults: posición en la lista a partir del cual comienza a devolver datos
    * @return lista de Entidad del tipo entityClass
    */
   protected List getLista(Class entityClass, boolean all, int maxResults, int firstResult) {
        try {
            Query q = (Query) getEntityManager().createQuery("select object(o) from "+entityClass.getSimpleName() +" as o");
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        }catch(Exception e){
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "getLista", e);                                    
            return null;
        }
    }

   /**
    * Consulta todos los registro de una entidad particular de acuerdo a un filtro determinado
    *
    * @param <T>
    * @param entityClass: Clase de la entidad a consultar
    * @param all: indica si se devuelven todos los registro
    * @param maxResults: cantidad de registros a devolver
    * @param maxResults: posición en la lista a partir del cual comienza a devolver datos
    * @param filtro aplicado a la consulta
    * @return lista de Entidad del tipo entityClass
    */
    public <T extends Object> T getLista(Class entityClass, boolean all, int maxResults, int firstResult, Map<String,String> filtro){

        try {
            String sQuery = "SELECT o FROM "+entityClass.getSimpleName() +" o ";
            sQuery.concat(generarStringFiltro(filtro,"o",true));

            Query q = em.createQuery(sQuery);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }

            return  (T) q.getSingleResult();

        }catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "getLista", e);           
            return null;
        }
   }

   /**
    * Consulta todos los registro de una entidad particular de acuerdo a un filtro determinado
    *
    * @param <T>
    * @param entityClass: Clase de la entidad a consultar
    * @param all: indica si se devuelven todos los registro
    * @param maxResults: cantidad de registros a devolver
    * @param firstResults: posición en la lista a partir del cual comienza a devolver datos
    * @param campoOrdena : nombre del campo por el cual queremos ordenar los datos
    * @param ordena: indica si se ordena o no
    * @param filtro aplicado a la consulta
    * @return lista de Entidad del tipo entityClass
    */
    public <T extends Object> T getLista(Class entityClass, boolean all, int maxResults, int firstResult,
                                         String campoOrdena, boolean ordena,
                                         Map<String,String> filtro){

        try {
            String sQuery = "SELECT o FROM "+entityClass.getSimpleName() +" o ";
            sQuery.concat(generarStringFiltro(filtro,"o",true));

            if(ordena) sQuery.concat(" ORDER BY " + campoOrdena);

            
            Query q = em.createQuery(sQuery);

            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }

            return  (T) q.getSingleResult();

        }catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "getLista", e);     
            return null;
        }
   }

   /**
    * Consulta en la cual se devuelve un solo objeto
    *
    * @param <T>
    * @param entityClass: Clase de la entidad devuelta
    * @param sQuery: consulta a ejecutar
    * @return Entidad del tipo entityClass
    */
   protected <T extends Object> T queryObject(Class<T> entityClass,String sQuery){

       try {
           em.flush();
           return (T) em.createQuery( sQuery).getSingleResult();
       } catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "queryObject", e);     
            return null;
       }
   }

   /**
    * Consulta en la cual se devuelven mas de un objeto
    *
    * @param entityClass : Clase de la entidad contenida en la lista devuelta
    * @param sQuery : Consulta a ejecutar
    * @return lista de Entidad del tipo entityClass
    */
   public List queryList(Class entityClass,String sQuery){

       try {
            return em.createQuery(sQuery).getResultList();
       } catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "queryList", e);     
            return null;
       }
   }

   /**
    * Consulta de actualización
    *     *
     * @param query
    */
   protected void queryUpdate(String query){
        try {
            getEntityManager().createQuery(query).executeUpdate();
        } catch (Exception e) {            
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "queryUpdate", e);     
        }

   }

   /**
    * Consulta la cantidad de registros de una entidad
    *
    * @param entityClass : Tipo de objetos contenidos en la lista devuelta    *
    * @return Cantidad de registros
    */
   public long getCantidadRegistros(Class entityClass) {
        return (Long) getEntityManager().createQuery("SELECT COUNT(o) FROM "+entityClass.getSimpleName() +" o").getSingleResult();
   }

   /**
    * Genera un string con la estructura JPQL para utilizarlo en una consulta
    *
    * @param filtro a aplicar a la consulta
    * @return
    */
   protected String generarStringFiltro(Map<String, String> filtro, String aliasEntidad, boolean cWhere) {

       if(filtro==null) return "";

       String sFiltro = "";
       //Si el filtro no está vacio lo aplicamos
       if (!filtro.isEmpty()) {

            Iterator it = filtro.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry ent = (Map.Entry) it.next();

                if(cWhere){
                    sFiltro += " WHERE ( "+ aliasEntidad+"."+ ent.getKey() + ent.getValue() + ") ";
                    cWhere = false;
                }else{
                    sFiltro += " AND ( "+ aliasEntidad+"."+ent.getKey() + ent.getValue() + ") ";
                }
            }
        }

       return sFiltro;

       /**
        //Si el filtro no está vacio lo aplicamos
        if (!filtro.isEmpty()){
            
            //Agrego WHERE al string?
            if(cWhere) sFiltro = sFiltro + " WHERE ";
            //Si no agrego el WHERE, tengo que agregar el AND al principio 
            //Ya que viene de otra consulta con WHERE
            else cAND = true;
            Iterator it = filtro.entrySet().iterator();
            while(it.hasNext()){

                Map.Entry ent = (Map.Entry)it.next();

                if (cAND) sFiltro = sFiltro + " AND " ;
                sFiltro = sFiltro + "( o."+ ent.getKey() +" LIKE '%" + ent.getValue() + "%' )";
                cAND = true;
            }
        }
        */
        
    }




}




